package cn.edu.tju.wallet.service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.edu.tju.core.model.User;
import cn.edu.tju.core.security.repository.UserRepository;
import cn.edu.tju.wallet.domain.VirtualWallet;
import cn.edu.tju.wallet.domain.Transaction;
import cn.edu.tju.wallet.domain.WithdrawRule;
import cn.edu.tju.wallet.dto.VipLevelDTO;
import cn.edu.tju.wallet.repository.TransactionRepository;
import cn.edu.tju.wallet.repository.VirtualWalletRepository;
import cn.edu.tju.wallet.repository.WithdrawRuleRepository;

@Service
public class VirtualWalletService {
  
    @Autowired
    private VirtualWalletRepository walletRepository;
  
    @Autowired
    private WithdrawRuleRepository withdrawRuleRepository;
  
    @Autowired
    private TransactionRepository transactionRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private VipLevelService vipLevelService;

    // 规则缓存
    private WithdrawRule cachedActiveRule;
    private long cacheTimestamp = 0;
    private static final long CACHE_DURATION = 5 * 60 * 1000;
    
    // 请求去重缓存
    private final Map<String, Long> requestCache = new ConcurrentHashMap<>();
    private static final long REQUEST_TIMEOUT = 30 * 1000;

    /**
     * 提现功能
     */
    @Transactional(rollbackFor = Exception.class)
    public VirtualWallet.WithdrawResult withdraw(Long userId, BigDecimal amount) {
        String requestKey = generateRequestKey(userId, amount);
        
        // 请求去重检查
        if (!acquireRequestLock(requestKey)) {
            throw new BusinessException("请求处理中，请勿重复提交");
        }
        
        try {
            // 1. 基础参数校验
            validateWithdrawParams(userId, amount);

            // 2. 查询钱包
            VirtualWallet wallet = walletRepository.findByUserId(userId)
                .orElseThrow(() -> new BusinessException("钱包不存在，请先充值开通钱包"));

            // 3. 查询提现规则
            WithdrawRule rule = getActiveWithdrawRule();
            
            // 4. 计算手续费和总额
            BigDecimal fee = rule.calculateFee(amount);
            BigDecimal totalDeduct = amount.add(fee);
            
            // 5. 余额验证
            if (wallet.getAvailableBalance().compareTo(totalDeduct) < 0) {
                throw new BusinessException(String.format("可用余额%.2f元不足，需要%.2f元", 
                    wallet.getAvailableBalance(), totalDeduct));
            }
            
            // 6. 执行提现
            VirtualWallet.WithdrawResult domainResult = wallet.withdraw(amount, rule);
            
            // 7. 保存钱包
            walletRepository.save(wallet);

            // 8. 记录交易流水
            Transaction transaction = createTransaction(wallet, amount, domainResult, rule);
            transactionRepository.save(transaction);

            // 9. VIP升级检查：提现增加用户累计消费
            updateUserVipLevelAfterWithdraw(userId, amount);

            return domainResult;

        } catch (BusinessException e) {
            // 业务异常直接抛出
            throw e;
        } catch (Exception e) {
            // 系统异常记录日志并抛出
            e.printStackTrace();
            throw new BusinessException("系统处理异常: " + e.getMessage());
        } finally {
            releaseRequestLock(requestKey);
        }
    }
    
    /**
     * 获取激活的提现规则
     */
    private WithdrawRule getActiveWithdrawRule() {
        long currentTime = System.currentTimeMillis();

        // 检查缓存是否有效
        if (cachedActiveRule != null && (currentTime - cacheTimestamp) < CACHE_DURATION) {
            return cachedActiveRule;
        }

        try {
            // 查询数据库获取最新激活规则
            Optional<WithdrawRule> ruleOpt = withdrawRuleRepository
                .findFirstByStatusOrderByIdDesc(WithdrawRule.RuleStatus.ACTIVE);

            if (!ruleOpt.isPresent()) {
                // 降级查询所有激活规则
                List<WithdrawRule> activeRules = withdrawRuleRepository.findByStatus(WithdrawRule.RuleStatus.ACTIVE);
                if (activeRules.isEmpty()) {
                    // 如果没有提现规则，创建一个默认规则
                    WithdrawRule defaultRule = new WithdrawRule();
                    defaultRule.setName("默认提现规则");
                    defaultRule.setFeeRate(new BigDecimal("0.10"));
                    defaultRule.setMinFee(new BigDecimal("1.00"));
                    defaultRule.setMaxFee(new BigDecimal("100.00"));
                    defaultRule.setStatus(WithdrawRule.RuleStatus.ACTIVE);
                    defaultRule.setPriority(1);

                    cachedActiveRule = defaultRule;
                    cacheTimestamp = currentTime;

                    return cachedActiveRule;
                }
                ruleOpt = activeRules.stream()
                    .max(Comparator.comparing(WithdrawRule::getId));
            }

            cachedActiveRule = ruleOpt.get();
            cacheTimestamp = currentTime;

            return cachedActiveRule;
        } catch (Exception e) {
            // 如果查询出错，创建默认规则
            System.err.println("查询提现规则失败，使用默认规则: " + e.getMessage());
            WithdrawRule defaultRule = new WithdrawRule();
            defaultRule.setName("默认提现规则");
            defaultRule.setFeeRate(new BigDecimal("0.10"));
            defaultRule.setMinFee(new BigDecimal("1.00"));
            defaultRule.setMaxFee(new BigDecimal("100.00"));
            defaultRule.setStatus(WithdrawRule.RuleStatus.ACTIVE);
            defaultRule.setPriority(1);

            cachedActiveRule = defaultRule;
            cacheTimestamp = currentTime;

            return cachedActiveRule;
        }
    }
    
    /**
     * 参数校验
     */
    private void validateWithdrawParams(Long userId, BigDecimal amount) {
        if (userId == null || userId <= 0) {
            throw new BusinessException("用户ID不合法");
        }
        if (amount == null) {
            throw new BusinessException("提现金额不能为空");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("提现金额必须大于0");
        }
        if (amount.scale() > 2) {
            throw new BusinessException("金额小数位数不能超过2位");
        }
        
        // 系统级基础限额
        if (amount.compareTo(new BigDecimal("0.01")) < 0) {
            throw new BusinessException("提现金额不能小于0.01元");
        }
        if (amount.compareTo(new BigDecimal("50000")) > 0) {
            throw new BusinessException("单笔提现金额不能超过50000元");
        }
    }
    
    /**
     * 创建交易记录
     */
    private Transaction createTransaction(VirtualWallet wallet, BigDecimal amount,
                                        VirtualWallet.WithdrawResult domainResult, WithdrawRule rule) {
        BigDecimal totalDeduct = amount.add(domainResult.getFee());
        
        Transaction transaction = new Transaction(
            wallet.getId(),
            Transaction.TransactionType.WITHDRAW,
            totalDeduct.negate(),
            wallet.getBalance(),
            wallet.getBalance().subtract(totalDeduct)
        );
        
        transaction.setStatus(Transaction.TransactionStatus.SUCCESS);
        transaction.setRemark(String.format("提现:%.2f元,手续费:%.2f元", amount, domainResult.getFee()));
        
        return transaction;
    }
    
    // 请求管理方法
    private String generateRequestKey(Long userId, BigDecimal amount) {
        return userId + ":" + amount.setScale(2, RoundingMode.HALF_UP);
    }

    private String generateRequestKey(String prefix, BigDecimal amount) {
        return prefix + ":" + amount.setScale(2, RoundingMode.HALF_UP);
    }
    
    private boolean acquireRequestLock(String requestKey) {
        long currentTime = System.currentTimeMillis();
        cleanExpiredRequests(currentTime);
        
        Long existingTime = requestCache.get(requestKey);
        if (existingTime != null && (currentTime - existingTime) < REQUEST_TIMEOUT) {
            return false;
        }
        
        requestCache.put(requestKey, currentTime);
        return true;
    }
    
    private void releaseRequestLock(String requestKey) {
        requestCache.remove(requestKey);
    }
    
    private void cleanExpiredRequests(long currentTime) {
        if (requestCache.size() > 1000) {
            Iterator<Map.Entry<String, Long>> iterator = requestCache.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Long> entry = iterator.next();
                if (currentTime - entry.getValue() > REQUEST_TIMEOUT) {
                    iterator.remove();
                }
            }
        }
    }

    /**
     * 提现成功后更新用户VIP等级
     * @param userId 用户ID
     * @param amount 提现金额（作为消费金额）
     */
    private void updateUserVipLevelAfterWithdraw(Long userId, BigDecimal amount) {
        try {
            // 重新从数据库查询最新用户数据
            User user = userRepository.findById(userId).orElse(null);
            if (user == null) return;

            // 增加累计消费金额
            user.addConsumption(amount);

            // 保存用户信息（包含新增的消费金额）
            user = userRepository.save(user);

            // 检查VIP升级
            VipLevelDTO newLevel = vipLevelService.upgradeUserVipLevel(user);

            // 保存升级后的用户信息
            user = userRepository.save(user);

            if (newLevel != null) {
                System.out.println(String.format(
                    "用户 %s 提现后VIP升级成功: %s (等级%d)",
                    user.getUsername(), newLevel.getName(), newLevel.getLevel()
                ));
            }
        } catch (Exception e) {
            // 记录日志但不中断流程
            System.err.println("VIP升级失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 冻结资金功能
     *
     * @param userId 用户ID
     * @param amount 冻结金额
     * @param reason 冻结原因
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean freeze(Long userId, BigDecimal amount, String reason) {
        String requestKey = generateRequestKey("freeze:" + userId, amount);

        // 请求去重检查
        if (!acquireRequestLock(requestKey)) {
            throw new BusinessException("请求处理中，请勿重复提交");
        }

        try {
            // 1. 基础参数校验
            validateFreezeParams(userId, amount, reason);

            // 2. 查询钱包
            VirtualWallet wallet = walletRepository.findByUserId(userId)
                .orElseThrow(() -> new BusinessException("钱包不存在，请先充值开通钱包"));

            // 3. 验证余额
            if (wallet.getAvailableBalance().compareTo(amount) < 0) {
                throw new BusinessException(String.format("可用余额%.2f元不足，无法冻结%.2f元",
                    wallet.getAvailableBalance(), amount));
            }

            // 4. 执行冻结
            wallet.freeze(amount);

            // 5. 保存钱包
            walletRepository.save(wallet);

            // 6. 记录交易流水
            Transaction transaction = createFreezeTransaction(wallet, amount, reason);
            transactionRepository.save(transaction);

            return true;

        } finally {
            releaseRequestLock(requestKey);
        }
    }

    /**
     * 解冻资金功能
     *
     * @param userId 用户ID
     * @param amount 解冻金额
     * @param reason 解冻原因
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean unfreeze(Long userId, BigDecimal amount, String reason) {
        String requestKey = generateRequestKey("unfreeze:" + userId, amount);

        // 请求去重检查
        if (!acquireRequestLock(requestKey)) {
            throw new BusinessException("请求处理中，请勿重复提交");
        }

        try {
            // 1. 基础参数校验
            validateUnfreezeParams(userId, amount, reason);

            // 2. 查询钱包
            VirtualWallet wallet = walletRepository.findByUserId(userId)
                .orElseThrow(() -> new BusinessException("钱包不存在，请先充值开通钱包"));

            // 3. 验证冻结余额
            if (wallet.getFrozenBalance().compareTo(amount) < 0) {
                throw new BusinessException(String.format("冻结余额%.2f元不足，无法解冻%.2f元",
                    wallet.getFrozenBalance(), amount));
            }

            // 4. 执行解冻
            wallet.unfreeze(amount);

            // 5. 保存钱包
            walletRepository.save(wallet);

            // 6. 记录交易流水
            Transaction transaction = createUnfreezeTransaction(wallet, amount, reason);
            transactionRepository.save(transaction);

            return true;

        } finally {
            releaseRequestLock(requestKey);
        }
    }

    /**
     * 冻结参数校验
     */
    private void validateFreezeParams(Long userId, BigDecimal amount, String reason) {
        if (userId == null || userId <= 0) {
            throw new BusinessException("用户ID不合法");
        }
        if (amount == null) {
            throw new BusinessException("冻结金额不能为空");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("冻结金额必须大于0");
        }
        if (amount.scale() > 2) {
            throw new BusinessException("金额小数位数不能超过2位");
        }
        if (reason == null || reason.trim().isEmpty()) {
            throw new BusinessException("冻结原因不能为空");
        }

        // 系统级基础限额
        if (amount.compareTo(new BigDecimal("0.01")) < 0) {
            throw new BusinessException("冻结金额不能小于0.01元");
        }
        if (amount.compareTo(new BigDecimal("100000")) > 0) {
            throw new BusinessException("单笔冻结金额不能超过100000元");
        }
    }

    /**
     * 解冻参数校验
     */
    private void validateUnfreezeParams(Long userId, BigDecimal amount, String reason) {
        if (userId == null || userId <= 0) {
            throw new BusinessException("用户ID不合法");
        }
        if (amount == null) {
            throw new BusinessException("解冻金额不能为空");
        }
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("解冻金额必须大于0");
        }
        if (amount.scale() > 2) {
            throw new BusinessException("金额小数位数不能超过2位");
        }
        if (reason == null || reason.trim().isEmpty()) {
            throw new BusinessException("解冻原因不能为空");
        }
    }

    /**
     * 创建冻结交易记录
     */
    private Transaction createFreezeTransaction(VirtualWallet wallet, BigDecimal amount, String reason) {
        BigDecimal balanceBefore = wallet.getBalance();
        BigDecimal availableBalanceBefore = wallet.getAvailableBalance();
        BigDecimal frozenBalanceBefore = wallet.getFrozenBalance();

        Transaction transaction = new Transaction(
            wallet.getId(),
            Transaction.TransactionType.FREEZE,
            amount.negate(), // 冻结金额记为负数，表示可用余额减少
            wallet.getAvailableBalance().add(amount), // 交易前可用余额
            wallet.getAvailableBalance() // 交易后可用余额
        );

        transaction.setStatus(Transaction.TransactionStatus.SUCCESS);
        transaction.setRemark(String.format("冻结:%.2f元,原因:%s", amount, reason));

        return transaction;
    }

    /**
     * 创建解冻交易记录
     */
    private Transaction createUnfreezeTransaction(VirtualWallet wallet, BigDecimal amount, String reason) {
        Transaction transaction = new Transaction(
            wallet.getId(),
            Transaction.TransactionType.UNFREEZE,
            amount, // 解冻金额记为正数，表示可用余额增加
            wallet.getFrozenBalance(), // 交易前冻结余额
            wallet.getFrozenBalance().subtract(amount) // 交易后冻结余额
        );

        transaction.setStatus(Transaction.TransactionStatus.SUCCESS);
        transaction.setRemark(String.format("解冻:%.2f元,原因:%s", amount, reason));

        return transaction;
    }

    /**
     * 自定义业务异常
     */
    public static class BusinessException extends RuntimeException {
        public BusinessException(String message) {
            super(message);
        }

        public BusinessException(String message, Throwable cause) {
            super(message, cause);
        }
    }
}